﻿using System.Collections.Generic;
using System.Reflection;
using PaintDotNet.Effects;
using PaintDotNet.IndirectUI;
using PaintDotNet.PropertySystem;
using System.Drawing;
using System.Drawing.Imaging;
using System.Resources;
using System.Globalization;

namespace Bilateral
{
    public class Bilateral : PropertyBasedEffect
    {
        private static ResourceManager rm = new ResourceManager("Bilateral.Localization", typeof(Bilateral).Assembly);

        private const string kernelParam = "kernel";
        private const string colorParam = "color";
        private const string spatialParam = "spatial";

        private static string GetEffectName()
        {
            return rm.GetString("name");
        }

        private static Image GetIcon()
        {
            return (new ResourceManager("Bilateral.Assets", typeof(Bilateral).Assembly)).GetObject("icon") as Image;
        }


        public Bilateral()
            : base(GetEffectName(), GetIcon(), SubmenuNames.Blurs, EffectFlags.Configurable | EffectFlags.SingleThreaded)
        {
        }

        protected override PropertyCollection OnCreatePropertyCollection()
        {
            var properties = new List<Property>();

            properties.Add(new PaintDotNet.PropertySystem.Int32Property(kernelParam, 9, 3, 55));
            properties.Add(new PaintDotNet.PropertySystem.Int32Property(colorParam, 5, 1, 100));
            properties.Add(new PaintDotNet.PropertySystem.Int32Property(spatialParam, 20, 1, 100));

            return new PropertyCollection(properties.ToArray());
        }

        protected override PaintDotNet.IndirectUI.ControlInfo OnCreateConfigUI(PropertyCollection props)
        {
            var configUI = PropertyBasedEffect.CreateDefaultConfigUI(props);

            configUI.SetPropertyControlValue(kernelParam, ControlInfoPropertyNames.DisplayName, rm.GetString("kernel"));
            configUI.SetPropertyControlValue(colorParam, ControlInfoPropertyNames.DisplayName, rm.GetString("color"));
            configUI.SetPropertyControlValue(spatialParam, ControlInfoPropertyNames.DisplayName, rm.GetString("spatial"));
            configUI.SetPropertyControlValue(spatialParam, ControlInfoPropertyNames.Description, rm.GetString("note"));
            
            return configUI;
        }

        private PropertyBasedEffectConfigToken oldToken = null;
        private int counter = 0;

        private static bool TokensEqual(PropertyBasedEffectConfigToken token1, PropertyBasedEffectConfigToken token2)
        {
            return token1.GetProperty(kernelParam).Value.Equals(token2.GetProperty(kernelParam).Value) &&
                   token1.GetProperty(colorParam).Value.Equals(token2.GetProperty(colorParam).Value) &&
                   token1.GetProperty(spatialParam).Value.Equals(token2.GetProperty(spatialParam).Value);
        }

        protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, PaintDotNet.RenderArgs dstArgs, PaintDotNet.RenderArgs srcArgs)
        {
            base.OnSetRenderInfo(newToken, dstArgs, srcArgs);

            // Paint.NET effects system is very rigid and doesn't let aplying filter in a single method call.
            // The bellow conditions are trying to understand when the user actualy clicks "OK" in the configuration dialog (rather then moves sliders) and after that start single pass filter aplication
            if ((counter > 1 && oldToken != null && TokensEqual(oldToken, newToken))) 
            {

                var rectangle = EnvironmentParameters.GetSelection(SrcArgs.Bounds).GetBoundsInt();

                int kernelSize = (int) Token.GetProperty(kernelParam).Value;
                kernelSize = kernelSize%2 == 0 ? kernelSize + 1 : kernelSize;
                int colorFactor = (int) Token.GetProperty(colorParam).Value;
                int spatialFactor = (int) Token.GetProperty(spatialParam).Value;

                var filter = new AForge.Imaging.Filters.Smooting.Bilateral();

                filter.LimitKernelSize = false;
                filter.KernelSize = (uint) kernelSize;
                filter.ColorFactor = colorFactor;
                filter.SpatialFactor = spatialFactor;

                filter.DisableParallelProcessing = false;

                BitmapData srcData = null;
                BitmapData dstData = null;

                try
                {
                    srcData = SrcArgs.Bitmap.LockBits(rectangle, ImageLockMode.ReadWrite, SrcArgs.Bitmap.PixelFormat);
                    dstData = DstArgs.Bitmap.LockBits(rectangle, ImageLockMode.ReadWrite, DstArgs.Bitmap.PixelFormat);
                    filter.Apply(new AForge.Imaging.UnmanagedImage(srcData),
                                 new AForge.Imaging.UnmanagedImage(dstData));
                }
                finally
                {
                    if (srcData != null) SrcArgs.Bitmap.UnlockBits(srcData);
                    if (dstData != null) DstArgs.Bitmap.UnlockBits(dstData);
                }

            }

            oldToken = newToken;
            counter++;
        }

        protected override void OnRender(Rectangle[] renderRects, int startIndex, int length)
        {
        }

    }
}
